/******************************************************************************/
/*                                                                            */
/*  Copyright (c) 2007-2009: ngi-central.org                                  */
/*  All rights reserved.                                                      */
/*                                                                            */
/******************************************************************************/


/// \file
/// This file implements data structures for moments calculations.


#ifndef __sgl_scale_invariant_moments_h__
#define __sgl_scale_invariant_moments_h__

#include <sgl/global_settings.h>
#include <sgl/conditions.h>
#include <sgl/central_moments.h>
#include <sgl/real_concept.h>
#include <boost/concept_check.hpp>

namespace sgl {


/******************************************************************************/
/*                                                                            */
/* scale_invariant_moments                                                    */
/*                                                                            */
/******************************************************************************/

template <
	/// The type. 
	//! 
	//! This type defaults to SGL_FPTYPE (which in turn defaults to double), but 
	//! any other numeric type is also suitable.
	typename T=SGL_FPTYPE>
/// Class to hold scale invariant moments up to the third order.
//!
//! The scale_invariant_moments are invariant with respect to translation and 
//! scaling, but not rotation.
//!
//! The following operations are implemented:
//! operator== : comparison for equality, directly implemented.
//! operator != : comparison for inequality, implemented through 
//! boost::equality_comaparable.
class scale_invariant_moments
	: boost::equality_comparable<sgl::scale_invariant_moments<T>, 
		sgl::scale_invariant_moments<T>
	>
{
	// Only real types qualify for type T. If this line triggers an error,
	// type T is not a real type.
	BOOST_CONCEPT_ASSERT((sgl::real_concept<T>));

public:
	/// The value type.
	typedef T value_type;

	/// Default constructor. 
	//!
	//! Zero initializes the moments.
	scale_invariant_moments()
		: eta20_(T())
		, eta11_(T())
		, eta02_(T())
		, eta30_(T())
		, eta21_(T())
		, eta12_(T())
		, eta03_(T())
	{ }

	/// Standard constructor.
	//!
	//! Initializes the moments.
	scale_invariant_moments(
		/// Second order horizontal moment.
		T eta20, 
		/// Second order mixed moment.
		T eta11,
		/// Second order vertical moment.
		T eta02,
		/// Third order horizontal moment.
		T eta30, 
		/// Third order horizontal mixed moment.
		T eta21,
		/// Third order vertical mixed moment.
		T eta12,
		/// Third order vertical moment.
		T eta03)
		: eta20_(eta20)
		, eta11_(eta11)
		, eta02_(eta02)
		, eta30_(eta30)
		, eta21_(eta21)
		, eta12_(eta12)
		, eta03_(eta03)
	{ }

	/// Constructs scale invariant moments from central moments.
	//!
	//! This calculates scale invariant moments from central moments.
	scale_invariant_moments(
		/// The right hand side.
		sgl::central_moments<T> const& r)
		: eta20_(r.calculate_eta20())
		, eta11_(r.calculate_eta11())
		, eta02_(r.calculate_eta02())
		, eta30_(r.calculate_eta30())
		, eta21_(r.calculate_eta21())
		, eta12_(r.calculate_eta12())
		, eta03_(r.calculate_eta03())
	{ }

	/// Constructs a copy.
	scale_invariant_moments(
		/// The right hand side.
		sgl::scale_invariant_moments<T> const& r)
		: eta20_(r.eta20_)
		, eta11_(r.eta11_)
		, eta02_(r.eta02_)
		, eta30_(r.eta30_)
		, eta21_(r.eta21_)
		, eta12_(r.eta12_)
		, eta03_(r.eta03_)
	{ }

	/// Copy assignment
	sgl::scale_invariant_moments<T> & operator =(
		/// The right hand side.
		sgl::scale_invariant_moments<T> rhs)
	{
		swap(rhs);
		return *this;
	}

	/// Exchanges two objects.
	void swap(
		/// The other object.
		sgl::scale_invariant_moments<T> & rhs)
	{
		std::swap(eta20_, rhs.eta20_);
		std::swap(eta11_, rhs.eta11_);
		std::swap(eta02_, rhs.eta02_);
		std::swap(eta30_, rhs.eta30_);
		std::swap(eta21_, rhs.eta21_);
		std::swap(eta12_, rhs.eta12_);
		std::swap(eta03_, rhs.eta03_);
	}

	/// Compare objects for equality.
	//!
	//! This instance is equal to another instance, if its moments are all equal 
	//! to the moments of the other instance.
	//!
	//! Through the use of boost::equality_comparable operator != is also
	//! defined and implemented by calling this operator.
	//!
	//! \return true if equal, false otherwise.
    bool operator ==(
		/// Right hand side.
		sgl::scale_invariant_moments<T> const& rhs) const
    {
        return 
			eta20_ == rhs.eta20_ && 
			eta11_ == rhs.eta11_ &&
			eta02_ == rhs.eta02_ && 
			eta30_ == rhs.eta30_ && 
			eta21_ == rhs.eta21_ &&
			eta12_ == rhs.eta12_ &&
			eta03_ == rhs.eta03_;
    }

	/// Access the horizontal second order central moment.
	//!
	//! /return Returns a reference to the horizontal second order central 
	//! moment.
	T & eta20()
	{
		return eta20_;
	}

	/// Access the horizontal second order central moment.
	//!
	//! /return Returns a const reference to the horizontal second order central 
	//! moment.
	T const & eta20() const
	{
		return eta20_;
	}

	/// Access the mixed second order central moment.
	//!
	//! /return Returns a reference to the mixed second order central moment.
	T & eta11()
	{
		return eta11_;
	}

	/// Access the mixed second order central moment.
	//!
	//! /return Returns a const reference to the mixed second order central 
	//! moment.
	T const & eta11() const
	{
		return eta11_;
	}

	/// Access the vertical second order central moment.
	//!
	//! /return Returns a reference to the vertical second order central moment.
	T & eta02()
	{
		return eta02_;
	}

	/// Access the vertical second order central moment.
	//!
	//! /return Returns a const reference to the vertical second order central 
	//! moment.
	T const & eta02() const
	{
		return eta02_;
	}

	/// Access the horizontal third order central moment.
	//!
	//! /return Returns a reference to the horizontal third order central 
	//! moment.
	T & eta30()
	{
		return eta30_;
	}

	/// Access the horizontal third order central moment.
	//!
	//! /return Returns a const reference to the horizontal third order central 
	//! moment.
	T const & eta30() const
	{
		return eta30_;
	}

	/// Access the first (horizontal) mixed third order central moment.
	//!
	//! /return Returns a reference to the first (horizontal) mixed third order 
	//! central moment.
	T & eta21()
	{
		return eta21_;
	}

	/// Access the first (horizontal) mixed third order central moment.
	//!
	//! /return Returns a const reference to the first (horizontal) mixed third 
	//! order central moment.
	T const & eta21() const
	{
		return eta21_;
	}

	/// Access the second (vertical) mixed third order central moment.
	//!
	//! /return Returns a reference to the second (vertical) mixed third order 
	//! central moment.
	T & eta12()
	{
		return eta12_;
	}

	/// Access the second (vertical) mixed third order central moment.
	//!
	//! /return Returns a const reference to the second (vertical) mixed third 
	//! order central moment.
	T const & eta12() const
	{
		return eta12_;
	}

	/// Access the vertical third order central moment.
	//!
	//! /return Returns a reference to the vertical third order central moment.
	T & eta03()
	{
		return eta03_;
	}

	/// Access the vertical third order central moment.
	//!
	//! /return Returns a const reference to the vertical third order central 
	//! moment.
	T const & eta03() const
	{
		return eta03_;
	}

	/// Calculate the first Hu moment from scale invariant moments.
	//!
	//! phi1 is the first scale and rotation invariant Hu moment.
	//!
	//! /return First scale and rotation invariant Hu moment.
	T calculate_phi1() const
	{
		return eta20() + eta02();
	}

	/// Calculate the second Hu moment from scale invariant moments.
	//!
	//! phi2 is the second scale and rotation invariant Hu moment.
	//!
	//! /return Second scale and rotation invariant Hu moment.
	T calculate_phi2() const
	{
		T a = eta20() - eta02();
		return a*a + T(4)*eta11()*eta11();
	}

	/// Calculate the third Hu moment from scale invariant moments.
	//!
	//! phi3 is the third scale and rotation invariant Hu moment.
	//!
	//! /return Third scale and rotation invariant Hu moment.
	T calculate_phi3() const
	{
		T a = eta30() - T(3)*eta12();
		T b = eta03() - T(3)*eta21();
		return a*a + b*b;
	}

	/// Calculate the fourth Hu moment from scale invariant moments.
	//!
	//! phi4 is the fourth scale and rotation invariant Hu moment.
	//!
	//! /return Fourth scale and rotation invariant Hu moment.
	T calculate_phi4() const
	{
		T a = eta30() + eta12();
		T b = eta03() + eta21();
		return a*a + b*b;
	}

	/// Calculate the fifth Hu moment from scale invariant moments.
	//!
	//! phi5 is the fifth scale and rotation invariant Hu moment.
	//!
	//! /return Fifth scale and rotation invariant Hu moment.
	T calculate_phi5() const
	{
		T a = eta30() - T(3)*eta12();
		T b = eta30() + eta12();
		T c = eta21() + eta03();
		T d = T(3)*eta21() - eta03();
		return a*b*(b*b - T(3)*c*c) + d*c*(T(3)*b*b - c*c);
	}

	/// Calculate the sixth Hu moment from scale invariant moments.
	//!
	//! phi6 is the sixth scale and rotation invariant Hu moment.
	//!
	//! /return Sixth scale and rotation invariant Hu moment.
	T calculate_phi6() const
	{
		T a = eta20() - eta02();
		T b = eta30() + eta12();
		T c = eta21() + eta03();
		return a*(b*b - c*c) + T(4)*eta11()*b*c;
	}

	/// Calculate the seventh Hu moment from scale invariant moments.
	//!
	//! phi7 is the seventh scale and rotation invariant Hu moment.
	//!
	//! /return Seventh scale and rotation invariant Hu moment.
	T calculate_phi7() const
	{
		T a = T(3)*eta21() - eta03();
		T b = eta30() + eta12();
		T c = eta21() + eta03();
		T d = eta30() - T(3)*eta12();
		return 
			a*b*(b*b - T(3)*c*c) - d*c*(T(3)*b*b - c*c);
	}

private:
	/// The horizontal second order central moment.
	T eta20_;

	/// The mixed second order central moment.
	T eta11_;

	/// The vertical second order central moment.
	T eta02_;

	/// The horizontal third order central moment.
	T eta30_;

	/// The first (horizontal) mixed third order central moment.
	T eta21_;

	/// The second (vertical) mixed third order central moment.
	T eta12_;

	/// The vertical third order central moment.
	T eta03_;
};


} // namespace sgl

#endif // __sgl_scale_invariant_moments_h__
